SQLite 数据库文件存储在设备上的 /data/data/package_name/databases 文件夹中,所有的数据库都市私有的,只能被创建者访问。
使用数据库时最好的做法是将底层数据库封装起来,只公开与该数据库进行交互时必须使用的公有方法和常量,这一般会用到所谓的辅助类。

ContentValue 和 Cursor

Content Value 用来向表中插入新的行数据,每个 ContentValues 对象都将一个行记录对应。
数据库查询结果作为 Cursor 对象返回。Cursor 是底层数据中的结果集的指针,它没有提取和返回结果值的副本。
Cursor 常用的几个方法:

  • moveToFirst
  • moveToNext
  • moveToPrevious
  • moveToPosition
  • getCount
  • getColumnIndexOrThrow
  • getColumnName
  • getColumnNames
  • getPosition

Android 提供了一种方便的机制,可以确保异步执行查询,API Level 11 引入了 CursorLoader 类和相关的 Loader Manager,现在他们已经成为了支持库的一部分,从而允许你在支持早期的 Android 版本的同时使用这些功能。


SQLiteOpenHelper

是一个抽象类,用来实现创建、打开和升级数据库。会缓存数据库实例。通过 onCreate 和 onUpgrade 方法来分别处理创建新数据库和升级新版本数据库。

等到需要数据库时在创建和打开这些数据库是一种很好的做法,SQLiteOpenHelper 会再打开数据库后缓存他的实例,一般情况下无需手动关闭数据库。

数据库操作(特别是打开或创建数据库)需要很长时间才能完成,为了确保不影响用户操作体验,应该使所有数据库事物异步执行。

通常可以使用 SQLiteOpenHelper 的 getWritableDatabase 或者 getReadableDatabase 来分别打开一个可写或只读的实例。如果数据库不存在,就会执行 onCreate 方法,如果数据库版本发生变化则会执行 onUpgrade 方法。

也可以不使用 SQLiteOpenHelper 打开数据库实例,可以通过 Context 对象的 openOrCreateDatabase 方法

public class HoardDBOPenHelper extends SQLiteOpenHelper {

    private static final String DATABASE_NAME = "myDatabase.db";
    private static final String DATABASE_TABLE = "GoldHoards";
    private static final int DATABASE_VERSION = 1;

    public static final String KEY_ID = "_id";

    public static final String KEY_GOLD_HOARD_NAME_COLUMN = "GOLD_HOARD_NAME_COLUMN";
    public static final String KEY_GOLD_HOARD_ACCESSIBLE_COLUMN = "OLD_HOARD_ACCESSIBLE_COLUMN";
    public static final String KEY_GOLD_HOARDED_COLUMN = "GOLD_HOARDED_COLUMN";

    private static final String DATABASE_CREATE = "create table "
        + DATABASE_NAME + "(" + KEY_ID
        + "integer primary key autoincrement, "
        + KEY_GOLD_HOARD_NAME_COLUMN + " text not null, "
        + KEY_GOLD_HOARDED_COLUMN + " float, "
        + KEY_GOLD_HOARD_ACCESSIBLE_COLUMN + " integer);";

    public HoardDBOPenHelper(Context context, String name,
        CursorFactory factory, int version,
        DatabaseErrorHandler errorHandler) {
        super(context, name, factory, version);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL(DATABASE_CREATE);
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        Log.w("TaskDBAdapter", "Upgrading from version " + oldVersion + " to "
        + newVersion + ", which will destroy all old data");

        db.execSQL("DROP TABLE IF IT EXISTS " + DATABASE_CREATE);

        onCreate(db);
    }
}

数据库设计注意事项

  • 每个表都增加一个 _id 作为每一行的索引字段。如果使用 Content Provider 就必须要有一个 唯一的 ID 字段。
  • 文件通常是不存在数据库中的,应该存放文件的路径地址。


SQLiteDatabase

数据库操作实例,提供了操作数据库的一切动作,包含增删改查、执行 SQL 语句。


数据库查询

每个数据库查询都会作为一个 Cursor 返回,这就使得 Android 可以按需检索和释放行和列的值,从而更加高效的管理资源。

要对一个数据库对象进行查询,需要使用 query 方法,并传入以下的一些参数:

  • 一个可选的布尔值,用来指定结果集是否只包含唯一的值。
  • 要查询的表的名称。
  • 一个字符串数组形式的投影,列出了包含在结果集中的列。
  • 一条 where 子句,定义了要返回的行。可以在其中包含“?” 通配符,它将会被通过选择参数传入的值替换。
  • 一个选择参数字符串的数组,它将会替换 where 子句中的“?”,通配符。
  • 一条 group by 子句,用来定义返回的行的分组方式。
  • 一条 having 子句,如果指定了一条 group by 子句,则盖子句会定义要包含那些行组。
  • 一个字符串,用来描述返回的行的顺序。
  • 一个字符串,用来定义结果集中的最大行数。
public void queryHoard() {
    String[] result_cloums = new String[] { KEY_ID,
        KEY_GOLD_HOARD_ACCESSIBLE_COLUMN, KEY_GOLD_HOARDED_COLUMN };

    // 指定用于限制结果的 where 子句
    String where = KEY_GOLD_HOARD_ACCESSIBLE_COLUMN + "=" + 1;

    // 根据需要把以下语句替换为有效的SQL语句
    String[] whereArgs = null;
    String groupBy = null;
    String having = null;
    String order = null;

    SQLiteDatabase db = this.getWritableDatabase();
    Cursor cursor = db.query(DATABASE_NAME, result_cloums, where,
        whereArgs, groupBy, having, order);
}


从 Cursor 中提取值

要 从 Cursor 中提取值,首先要使用前面描述过的 moveTo 系列的方法将游标放到结果 Cursor 的正确行中,然后使用类型安全的 get 方法来返回存储在指定列的当前行中的值。为了找到特定列在游标中的索引,需要使用 getColumnIndex 和 getColumnIndexOrThrow 方法。
当你认为列在所有情况下都存在时,使用 getColumnIndexOrThrow 是一种不错的办法,当列有可能不存在游标中时,使用 getColumnIndex 可以检查结果是否为 -1 比捕获异常更加高效。

int columnIndex = cursor.getColumnIndex(KEY_GOLD_HOARDED_COLUMN);
if (columnIndex > -1) {
    String columnValue = cursor.getColumnName(columnIndex);
    // 存在时
} else {
    // 不存在
}

从 Cursor 中获取值:

float totalHoard = 0f;
float averageHoard = 0f;

// 找出所用列的索引
int GOLD_HOARDED_COLUMN_INDEX = cursor.getColumnIndexOrThrow(KEY_GOLD_HOARD_NAME_COLUMN);

// 遍历游标
while (cursor.moveToNext()) {
    float hoard = cursor.getFloat(GOLD_HOARDED_COLUMN_INDEX);
    totalHoard += hoard;
}

// 计算平均值
float cursorCount = cursor.getCount();
averageHoard = cursorCount > 0 ? (totalHoard / cursorCount) : Float.NaN;

// 完成之后关闭游标
cursor.close();


添加、更新和删除

SQLiteDatabase 提供了 insert、delete、update 方法来封装执行这些操作需要的 SQL 语句。如果你希望手动去执行这些操作,需要用到 execSQL 方法可以对数据库表执行有效的 SQL 语句。

1. 插入行

// 创建一个要插入的行新值
ContentValues contentValues = new ContentValues();

// 为每行赋值
contentValues.put(KEY_GOLD_HOARD_ACCESSIBLE_COLUMN, "");
contentValues.put(KEY_GOLD_HOARD_NAME_COLUMN, 123);
contentValues.put(KEY_GOLD_HOARDED_COLUMN, true);

// 把行插入到表中
SQLiteDatabase sdb = this.getReadableDatabase();
sdb.insert(this.DATABASE_TABLE, null, contentValues);

insert 方法的第二个参数称为 null 列侵入(null column hack),如果想添加一个空行,在传入 ContentValues 还必须传入一个显示的 null。

2. 更新行

// 创建更新行的 ContentValues
ContentValues updatedValues = new ContentValues();

// 为每一行赋值
updatedValues.put(KEY_GOLD_HOARDED_COLUMN, false);

// 指定一个 where 子语句定义那些行应该被更新
String where = KEY_ID + "=" + 1;
String[] whereArgs = null;

// 使用新值更新数据库记录
SQLiteDatabase sdb = getReadableDatabase();
sdb.update(DATABASE_TABLE, updatedValues, where, whereArgs);

3. 删除行

// 指定一个 where 子语句定义那些行应该被删除
String where = KEY_ID + "=" + 1;
String[] whereArgs = null;

// 使用新值更新数据库记录
SQLiteDatabase sdb = getReadableDatabase();
sdb.delete(DATABASE_TABLE, where, whereArgs);

ytwman
294 声望7 粉丝